package com.wilschrader.demarche;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Set;

import com.wilschrader.demarche.Fleet.Type;
import com.wilschrader.demarche.models.DefaultDamageModel;
import com.wilschrader.demarche.util.MersenneTwisterFast;

/**
 * Runs the simulation!
 * 
 * @author Wil Schrader
 * @modifications
 *  08172011	DamageModel will temporarily be set from here.
 * 	08112011	Init revision.
 * 
 */
public class Simulation {
	
	private static boolean debugOutput = false;
	private static DamageModel damageModel = new DefaultDamageModel();
	
	LinkedHashMap<Fleet.Type, Fleet> fleets = new LinkedHashMap<Fleet.Type, Fleet>();
	
	ArrayList<Stage> stages = new ArrayList<Stage>();
	
	/**
	 * Add a fleet to the simulation.
	 * 
	 * @param type		The type of fleet that we are going to be adding.
	 * @param fleet		The actual fleet we are adding.
	 * @return			True if the fleet was added.
	 */
	public boolean addFleet(Fleet.Type type, Fleet fleet) {
		if (!fleets.containsKey(type)) {
			Simulation.debugOutput("Adding Fleet (" + fleet.getName() + ") to Simulation.");
			fleets.put(type, fleet);
			return true;
		}
		return false;
	}
	
	/**
	 * Place a combat stage at the bottom of the stack of combat stages.
	 * 
	 * @param stage
	 */
	public void addStage(Stage stage) {
		stages.add(stage);
	}
	
	/**
	 * Add a combat stage to a specific position.
	 * 
	 * @param stage
	 * @param position
	 */
	public void addStage(Stage stage, int position) {
		stages.add(position, stage);
	}
	
	/**
	 * A very simple simulation.
	 */
	public void runSimulation() {
		MersenneTwisterFast mt = new MersenneTwisterFast();
		for (Stage stage : stages) {
			Simulation.debugOutput("Executing Stage (" + stage.getClass().getSimpleName() + ") in Simulation.");
			stage.execute(mt, fleets);
		}
		
		analyze();
	}
	
	public void analyze() {
		Set<Type> fleetSet = fleets.keySet();

		for (Type type : fleetSet) {
			Fleet fleet = fleets.get(type);
			
			if (fleet.getAllActiveUnits().size() == 0) {
				System.out.println("Fleet (" + fleet.getName() + ") has no remaining units.");
			} else {
				System.out.println("Fleet (" + fleet.getName() + ") has " + fleet.getAllActiveUnits().size() + " remaining units.");
			}
		}
	}
	
	public void runSimulation(int times) {
		int attackingWins = 0;
		int offendingWins = 0;
		
		//MersenneTwisterFast mt = new MersenneTwisterFast();
		
		for (int i = 0; i < times; i++) {
			MersenneTwisterFast mt = new MersenneTwisterFast();
			for (Stage stage : stages) {
				Simulation.debugOutput("Executing Stage (" + stage.getClass().getSimpleName() + ") in Simulation.");
				stage.execute(mt, fleets);
			}
			
			analyze();
			
			if (fleets.get(Fleet.Type.Attacking).getActiveUnits(Unit.SPACE_UNITS).size() == 0)
				offendingWins++;
			else
				attackingWins++;
			
			Set<Type> fleetSet = fleets.keySet();
			for (Type type : fleetSet) {
				fleets.get(type).reset();
			}
		}
		
		System.out.println("Attacking fleet won " + attackingWins + " out of " + times + " simulations.");
	}
	
	public static void setDamageModel(DamageModel damageModel) {
		Simulation.damageModel = damageModel;
	}
	
	public static DamageModel getDamageModel() {
		return Simulation.damageModel;
	}
	
	/**
	 * If debug output is enabled, then for every action within the simulation
	 * a distinct string is written to standard out detailing what is happening.
	 * 
	 * @param debug	The string to be written.
	 * @return		True if written, false otherwise.
	 */
	public static boolean debugOutput(String debug) {
		if (debugOutput) {
			System.out.println(debug);
			return true;
		}
		return false;
	}

	public static void setDebugOutput(boolean debugOutput) {
		Simulation.debugOutput = debugOutput;
	}
	
	public static boolean isDebugOutputEnabled() {
		return Simulation.debugOutput;
	}
}
